
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>Kubernetes HPA 的三个误区与避坑指南</title>
            </head>
            <body>
            <a href="https://andyoung.blog.csdn.net">原作者博客</a>
            <div id="content_views" class="markdown_views prism-atom-one-light">
                    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
                        <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
                    </svg>
                    <blockquote> 
 <p>云计算带来的优势之一便是弹性能力，云原生场景下 Kubernetes 提供了水平弹性扩容能力（HPA），让应用可以随着实时指标进行扩/缩。然而 HPA 的实际工作情况可能和我们直观预想的情况是不一样的，这里面存在一些认知误区。</p> 
</blockquote> 
<h3><a id="HPA__2"></a>误区一：HPA 存在扩容死区</h3> 
<p><strong>现象</strong>：当 Request=Limit 时，期望利用率超过 90%时，无法正常扩容。</p> 
<p><strong>原因剖析</strong>：HPA 中存在容忍度（默认为 10%），指标变化幅度小于容忍度时，HPA 会忽略本次扩/缩动作。若当期望利用率为 90%时，则实际利用率在 81%-99%之间，都会被 HPA 忽略。</p> 
<p><strong>避坑指南</strong>：当 Request=Limit 时，避免设置过高的期望利用率，一来避免扩容死区；二来被动扩容有一定的迟滞时间，留下更多的缓冲余量以应对突增流量。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/72de804de69b272e99e659751d3ae449.jpeg" alt="图片"></p> 
<h3><a id="HPA__14"></a>误区二：误解利用率计算方法，HPA 扩容与预期使用量不符</h3> 
<p><strong>现象</strong>：当 Limit &gt; Request 时，配置 50%的利用率，使用量未达到 Limit 的 50%便扩容。</p> 
<p><strong>原因剖析</strong>：HPA 计算利用率是基于 Request 计算，当 Limit &gt; Request 时，实际利用率是可以超过 100%。</p> 
<p><strong>避坑指南</strong>：对于较为重要的应用，应当设置 Request=Limit 保证资源的独占。对于可以容忍资源共享的应用，对应的期望利用率也不应设置的过高，在集群资源紧张时，超量使用资源的 Pod 很有可能会被杀死，从而造成服务中断。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/a09d6b5ec7afe7d1a69b6162c4843407.jpeg" alt="图片"></p> 
<h3><a id="_24"></a>误区三：弹性行为总是滞后的，扩缩行为与心理预期不符</h3> 
<p><strong>现象</strong>：指标突增时，HPA 不会立刻扩容，且扩容可能是分多次进行，最终稳定时的实例数也与预期不同。</p> 
<p><strong>原因剖析</strong>：HPA 的设计架构决定了，HPA 扩/缩容总是滞后的，且扩/缩容收到弹性行为（behavior）与<strong>容忍度</strong>共同作用。其中弹性行为限制了扩/缩容速率，不会一口气扩/缩到期望实例数。而容忍度会忽略指标的小幅度变化，从而导致在多次扩容的场景下，最终计算的实例数可能与一开始计算出的实例数不同。</p> 
<p><strong>避坑指南</strong>：阅读下文了解一下 HPA 工作原理，配置合理的弹性行为（behavior）。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/4b932fd53c07437117f6ad4e7f2614dd.jpeg" alt="图片"></p> 
<h2><a id="HPA__34"></a><em><strong>HPA 工作机理</strong></em></h2> 
<p>在打破认知误区前，我们有必要梳理一下 HPA 的工作机理。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/eccdc60f36932bdf514f1d65e0f4f473.jpeg" alt="图片"></p> 
<p>如图所示，HPA 控制器执行弹性功能主要分为四个步骤：</p> 
<ol><li> <p>监听 HPA 资源，一旦生成 HPA 资源或者是更改 HPA 配置，HPA 控制器能及时感知并调整。</p> </li><li> <p>从 Metrics API 获取对应的指标数据，这里的 Metrics Server 又可以分为三类：</p> 
  <ul><li> <p>a. Kubernetes MetricServer：提供容器级别CPU/内存使用量</p> </li><li> <p>b. Custom MetricServer：提供来自Kubernetes集群自定义资源的指标数据</p> </li><li> <p>c. External MetricServer：提供来自Kubernetes集群外的指标数据</p> </li></ul> </li><li> <p>每个指标项单独计算期望实例数，最后取所有期望实例数中的最大值，作为当前工作负载的期望实例数</p> </li><li> <p>调整对应的工作负载</p> </li></ol> 
<p>其中步骤 2-4 约每 15 秒执行一次，如需改变时间周期，可以调整 KCM 的配置参数–horizontal-pod-autoscaler-sync-period。</p> 
<h3><a id="01__66"></a><strong>01</strong> <strong>数据源</strong></h3> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/222a0088e2941a414623c734c8385644.jpeg" alt="图片"></p> 
<p>如上图所示，HPA 目前提供了五种指标来源，以及三种指标服务（MetricsServer），简单介绍如下：</p> 
<ol><li> <p>Resource：提供 Pod 级别的 CPU/内存使用量</p> </li><li> <p>ContainerResource：提供容器级别的 CPU/内存使用量</p> </li><li> <p>Object：提供 Kubernetes 集群内任意资源的相关指标</p> </li><li> <p>Pods：提供 Kubernetes集群内 pod 相关的指标</p> </li><li> <p>External：提供 Kubernetes 集群外的指标数据</p> </li></ol> 
<p>值得一提的是，在自建 Kubernetes 场景下，这三种 MetricsServer 都需要额外安装，它们均运行于 KCM 之外。下表列举了几种 Kubernetes 集群 MetricsServer 的部署情况。</p> 
<h3><a id="02__86"></a><strong>02</strong> <strong>指标计算方法</strong></h3> 
<p>HPA 提供了三种期望值类型：</p> 
<ol><li> <p>总量（Value）</p> </li><li> <p>平均量（AverageValue）= 总量 / 当前实例数</p> </li><li> <p>利用率（Utilization）= 平均量 / Request</p> </li></ol> 
<p>值得一提的是，利用率是基于 Request 进行计算的，所以没有设置 Request 的场景下，HPA 可能无法正常工作。</p> 
<p>下图介绍了五种指标来源支持的期望类型，不难看出所有指标来源都支持平均量。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/b98e07f618bab5be97fbe79a77e25878.jpeg" alt="图片"></p> 
<p>对于单个指标的期望实例数计算规则如下：</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/23dfdc564b5bfc4a53fa17216d783bd2.png" alt="图片"></p> 
<p>这里面引入了容忍度的概念，即认为在期望值附近小范围的抖动是可以容忍忽略的。这个参数的来源是因为指标值是一个一直在抖动变化的值，如果不忽略微小的变动，那么很有可能造成应用不断的扩容缩容，进而影响整个系统的稳定性。</p> 
<p>如下图所示，当指标值落入粉色区域内（容忍度范围）时，期望实例数等于当前实例数。粉色区域（容忍度范围）的上下限分别是 0.9 倍期望值与 1.1 倍期望值。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/a1e675be279e6546665d3fca42ac4fc4.jpeg" alt="图片"></p> 
<p>对于配置了多条指标规则，最终期望实例数计算规则如下：</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/47e6e8c7a5741bdb7a574b56d12af736.png" alt="图片"></p> 
<p>其中 target1, …, target n 分别是每个指标计算出来的期望实例数。</p> 
<p><strong>用一句话简要概括计算方法</strong>：单个指标波动小时忽略不计，多个指标之间取最大值，最终实例数会落在下限和上限之间。</p> 
<h3><a id="03__122"></a><strong>03</strong> <strong>扩缩行为</strong></h3> 
<p>在某些情况下，指标数据会有一个频繁且大幅度的抖动。如下图所示的一段 CPU 指标数据，存在一些指标抖动或间歇流量下降导致利用率下降，指标的变化范围已经超出了容忍度的范围。此时，从应用稳定性角度来看，我们不期望应用缩容。为了解决这个问题，HPA引入了配置来控制扩缩容，即扩缩行为（behavior），它是在HPA（autoscaling/v2beta2）中引入，要求 Kubernetes 集群版本&gt;=1.18。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/2697f5f3db1933b8740fce0c4d18d394.jpeg" alt="图片"></p> 
<p>HPA 的弹性行分为扩容行为和缩容行为。行为具体由以下三部分组成：</p> 
<ul><li> <p><strong>稳定窗口</strong>：稳定窗口会参考过去一段时间计算出的期望实例数，选取极值作为最终结果，从而保证系统在一段时间窗口内是稳定的。对于扩容取极小值，对于缩容取极大值。</p> </li><li> <p><strong>步长策略</strong>：限制一段时间内实例变化的范围。由步长类型、步长值、时间周期三个部分组成。值得一提的是时间周期这个概念与上述的稳定窗口是两回事，此处的时间周期定义了回溯多长历史时间，计算实例数变化情况。</p> </li><li> <p><strong>选择策略</strong>：用于选取多个步长策略计算后的结果，支持 取最大值、取最小值、关闭 这三种策略。</p> </li></ul> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/0d82942253528c1c0120bc092d3ce590.jpeg" alt="图片"></p> 
<h2><a id="_144"></a>回顾与总结</h2> 
<p>至此，我们已经大致了解了 HPA 的工作机理。合理利用 HPA 可以有效提升资源利用率，在这之中我们总结了一些注意事项，熟记这些点可以在使用 HPA 时“有效避坑”。</p> 
<ol><li> <p>HPA 的设计架构导致了 HPA 只能被动响应指标进行弹性扩缩，这种模式下，<strong>弹性滞后是一定存在的</strong>。</p> </li><li> <p>HPA 的利用率计算方法是基于 Request，实际利用率/期望利用率超过 100%是正常的，配置较高的期望利用率需要合理规划集群资源和审视相应风险。</p> </li><li> <p>HPA 中的容忍度概念能缓解指标波动带来的系统震荡问题，但与此同时引入的扩容死区问题需要运维人员避开。</p> </li><li> <p>HPA 的设计架构允许扩展各种类型指标，需要开发/安装相应的 MetricsServer。</p> </li><li> <p>HPA 中存在扩缩容行为，即使不配置相应参数也有默认行为，扩容行为的稳定窗口默认是 0，如果应用常因噪声数据造成扩容，可以设置一个较短的扩容稳定窗口规避尖锐噪声。</p> </li><li> <p>单个 HPA 支持配置多个指标进行弹性，切勿对单个应用配置多个 HPA，会相互影响，导致应用震荡。</p> </li></ol> 
<p>云原生场景下弹性能力更为丰富，可供弹性的指标也更具备业务定制能力。应用 PaaS 平台（如企业级分布式应用服务 EDAS）能结合云厂商在计算、存储、网络上的技术基础能力，能让使用云的成本更低。但是这里对于业务应用会提出一点点挑战（如：无状态/配置代码解耦等等）。从更广的侧面来看，这是云原生时代应用架构面临的挑战。不过应用越来越原生的话，云的技术红利也会离我们越来越近。</p> 
<h2><a id="_166"></a><em>参考链接</em></h2> 
<h2><a id="_168"></a></h2> 
<h4><a id="1_Prometheus_Adapter_170"></a><em>[1] Prometheus Adapter</em></h4> 
<p><em>https://github.com/kubernetes-sigs/prometheus-adapter</em></p> 
<h4><a id="2_KEDA_174"></a><em>[2]</em> <em>KEDA</em></h4> 
<p><em>https://github.com/kedacore/keda</em></p> 
<h4><a id="3_HorizontalPodAutoscaler_Walkthrough_178"></a><em>[3]</em> <em>HorizontalPodAutoscaler Walkthrough</em></h4> 
<p><em>https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/</em></p> 
<h4><a id="4_Resource_metrics_pipeline_182"></a><em>[4]</em> <em>Resource metrics pipeline</em></h4> 
<p><em>https://kubernetes.io/docs/tasks/debug/debug-cluster/resource-metrics-pipeline/</em></p> 
<h4><a id="5_Horizontal_Pod_Autoscaling_186"></a><em>[5]</em> <em>Horizontal Pod Autoscaling</em></h4> 
<p><em>https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/</em></p> 
<h4><a id="6_HPA__190"></a><em>[6]</em> <em>HPA 常见问题与诊断</em></h4> 
<p><em>https://help.aliyun.com/document_detail/186980.html</em></p> 
<h4><a id="7_EDAS__194"></a><em>[7]</em> <em>EDAS 自动弹性扩缩容</em></h4> 
<p><em>https://help.aliyun.com/document_detail/178448.html</em></p> 
<h4><a id="_198"></a><em>扩展阅读</em></h4> 
<h4><a id="Horizontal_Pod_Autoscaler_with_Arbitrary_Metrics_200"></a><em>Horizontal Pod Autoscaler with Arbitrary Metrics</em></h4> 
<p><em>https://github.com/kubernetes/design-proposals-archive/blob/main/autoscaling/hpa-v2.md</em></p>
                </div>
            </body>
            </html>
            